//给定一个数组 candidates 和一个目标数 target ，找出 candidates 中所有可以使数字和为 target 的组合。 
//
// candidates 中的每个数字在每个组合中只能使用一次。 
//
// 说明： 
//
// 
// 所有数字（包括目标数）都是正整数。 
// 解集不能包含重复的组合。 
// 
//
// 示例 1: 
//
// 输入: candidates = [10,1,2,7,6,1,5], target = 8,
//所求解集为:
//[
//  [1, 7],
//  [1, 2, 5],
//  [2, 6],
//  [1, 1, 6]
//]
// 
//
// 示例 2: 
//
// 输入: candidates = [2,5,2,1,2], target = 5,
//所求解集为:
//[
//  [1,2,2],
//  [5]
//] 
// Related Topics 数组 回溯算法 
// 👍 342 👎 0

package leetcode.editor.cn;

import java.util.*;

//Java：组合总和 II
public class P40CombinationSumIi {

    //leetcode submit region begin(Prohibit modification and deletion)

    /**
     * 思路：回溯，自己写的版本到了去重那边就卡住了，而且代码有点奇怪，然后看了网上的代码
     * <p>
     * <p>
     * 执行用时： 3 ms , 在所有 Java 提交中击败了 88.06% 的用户 内存消耗： 40.2 MB , 在所有 Java 提交中击败了 18.99% 的用户
     */
    class Solution {
        private List<List<Integer>> list = new ArrayList<>();

        public List<List<Integer>> combinationSum2(int[] candidates, int target) {
            if (candidates != null && candidates.length != 0) {
                Arrays.sort(candidates);
                dfs(target, 0, candidates, new Stack<>());
            }
            return list;
        }

        // 1 1 2 5 6 7 10

        /**
         * 描述： 回溯算法
         *
         * @param t          剩余的差值，每次选中一次数字就用 t-那个数字，为0的时候，说明刚刚好，<0需要再选数字
         * @param k          上一次循环的下标
         * @param candidates 剩下待选的数字
         * @param stack      当前已选结果存起来
         * @date: 2020/8/25 15:12
         */
        private void dfs(int t, int k, int[] candidates, Stack<Integer> stack) {
            if (t == 0) {
                list.add(new ArrayList<>(stack));
                return;
            }

            for (int i = k; i < candidates.length; i++) {
                if (t < candidates[i]) {
                    break;
                }
                if (i > k && candidates[i] == candidates[i - 1]) {
                    continue;
                }
                stack.add(candidates[i]);
                dfs(t - candidates[i], i + 1, candidates, stack);
                stack.pop();
            }
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    public static void main(String[] args) {
        Solution solution = new P40CombinationSumIi().new Solution();
        int[] candidates = new int[]{10, 1, 2, 7, 6, 1, 5};
        solution.combinationSum2(candidates, 8);
        // TO TEST
    }

}